package net.bungaeting.client.android.task;

import java.util.AbstractQueue;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;

import net.bungaeting.client.android.request.HttpRequestor;
import net.bungaeting.client.android.request.Request;
import net.bungaeting.client.android.request.RequestObserver;
import net.bungaeting.client.android.request.Requestor;
import net.bungaeting.client.android.request.Response;
import net.bungaeting.client.android.request.RequestObserver.THREAD_TYPE;
import net.bungaeting.client.android.utils.ServiceConst;
import net.bungaeting.client.android.utils.ServiceUtil;
import android.os.AsyncTask;
import android.util.Log;

public class BackgroundService extends AsyncTask<Request, BackgroundServiceProgress, BackgroundServiceResult> {
	
	private final static int MAX_ACITVE_REQUEST_COUNT_DEFAULT = 3;
	private static final String SERVICE_ID_POLL_OBSERVING = "pollObserving";
	private static final int POLL_OBSERVING_TICK = 10;
	private final int maxAcitveRequestCount;
	private AbstractQueue<Request> queueRequest;
	private AbstractQueue<Response> queueResponse;
	private Map<String, RequestObserver> mapRequestObserver;
	private Requestor requestor;
	private Vector<Thread> activeRequest;
	private boolean enabled;
	
	public BackgroundService() {
		this(MAX_ACITVE_REQUEST_COUNT_DEFAULT);
	}
	public BackgroundService(int maxAcitveRequestCount) {
		this.maxAcitveRequestCount = maxAcitveRequestCount;
		queueRequest = new ConcurrentLinkedQueue<Request>();
		queueResponse = new ConcurrentLinkedQueue<Response>();
		mapRequestObserver = Collections.synchronizedMap(new HashMap<String, RequestObserver>());
		activeRequest = new Vector<Thread>();
		enabled = true;
		requestor = new HttpRequestor();
		pollObservingTick = POLL_OBSERVING_TICK;
	}

	@Override
	protected BackgroundServiceResult doInBackground(
			Request... arg0) {
		while (enabled){
			while (canProcessRequest() && processReqeust());
			
			while (processResponse());
			try {
				Thread.sleep(getSleepTime());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			requestPollObserving();
			//Log.d("TEST", "doInBackground loop");
		}
		return null;
	}
	
	protected long getSleepTime(){
		return 1000;
	}
	
	protected boolean canProcessRequest(){
		return activeRequest.size() < this.maxAcitveRequestCount;
	}
	protected boolean processReqeust(){
		if (queueRequest.size() == 0)
			return false;
		
		if (!canProcessRequest())
			return false;
		
		final Request request = dequeueRequest();
		Thread requestThread  = new Thread(new Runnable(){
			@Override
			public void run() {
				try {
					Thread.sleep(5L);
				} catch (InterruptedException e) {
					System.err.println("processRequest sleep failed.");
					e.printStackTrace();
				}
				List<Response> responseList = requestor.doRequest(request);
				Log.d("TEST", "responseList " + responseList.size());
				enqueueResponse(responseList);
				
				activeRequest.remove(Thread.currentThread());
			}
		});
		requestThread.start();
		activeRequest.add(requestThread);
		
		if (queueRequest.size() > 0)
			return true;
		else
			return false;
	}
	protected boolean processResponse(){
		if (queueResponse.size() == 0)
			return false;
		
		Response response = dequeueResponse();
		RequestObserver requestObserver = mapRequestObserver.get(response.getServiceId());
		if (requestObserver != null){
			if (requestObserver.getProcessThreadType() == RequestObserver.THREAD_TYPE.UI_THREAD){
				publishProgress(
						new BackgroundServiceProgress(
								BackgroundServiceProgress.TYPE.RESPONSE_PROCESS, response));
			}
			else{
				processRequestObserverUpdate(response);
			}
			processPollOvservingResponse();
		}
		Log.d("TEST", "processResponse end");
		if (queueResponse.size() > 0)
			return true;
		else
			return false;
	}
	protected boolean processPollOvservingResponse(){
		sleepPollObservingTick = 0;
		return true;
	}
	protected void processRequestObserverUpdate(Response response){
		RequestObserver requestObserver = mapRequestObserver.get(response.getServiceId());
		requestObserver.update(response.getRequestId(), response.getReturnCode(), response.getReturnMsg(), response.getParams());
	}
	
	@Override
	protected void onProgressUpdate(BackgroundServiceProgress... progresses) {
		for (BackgroundServiceProgress progress : progresses){
			processRequestObserverUpdate(progress.getResponse());
		}
		super.onProgressUpdate(progresses);
	}

	protected boolean enqueueRequest(Request request){
		queueRequest.offer(request);
		return true;
	}
	protected Request dequeueRequest(){
		return queueRequest.poll();
	}
	
	public boolean enqueueResponse(List<Response> responseList){
		for (Response response : responseList){
			Log.d("TEST", "enqueue return " + queueResponse.offer(response));
		}
		return true;
	}
	protected Response dequeueResponse(){
		return queueResponse.poll();
	}
	
	public void startWorkerThread(){
		this.execute();
	}
	public void stopWorkerThread(){
		enabled = false;
	}
	public void cancelWorkerThread(){
		this.cancel(true);
	}
	public boolean addObserver(String requestId, String serviceId, RequestObserver observer){
		if (observer != null){
			mapRequestObserver.put(serviceId + ServiceConst.SERVICE_ID_SUFFIX_OBSERVE, observer);
		}
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("serviceId", serviceId);
		params.put("clientId", ServiceUtil.getClientId());
		return enqueueRequest(Request.createRequest("addObserver", requestId, params));
	}
	
	public boolean requestAsync(String serviceId, String requestId, Map<String, Object> params, RequestObserver observer){
		if (observer != null){
			mapRequestObserver.put(serviceId, observer);
		}
		return enqueueRequest(Request.createRequest(serviceId, requestId, params));
	}
//	public boolean requestSync(String serviceId, String requestId, Map<String, Object> params, RequestObserver observer){
//		List<Response> responseList = requestor.doRequest(Request.createRequest(serviceId, requestId, params));
//		RequestObserver requestObserver = mapRequestObserver.get(response.getServiceId());
//		requestObserver.update(response.getRequestId(), response.getReturnCode(), response.getReturnMsg(), response.getParams());
//		return true;
//	}

	static public String getServiceRequestId(String serviceId, String requestId){
		return serviceId + "." + requestId;
	}
	
	private Request observingRequest;
	private int pollObservingTick;
	private int sleepPollObservingTick;
	private void requestPollObserving(){
		if (observingRequest == null){
			mapRequestObserver.put(SERVICE_ID_POLL_OBSERVING, null);
			observingRequest = createPollObservingRequest();
			sleepPollObservingTick = 0;
		}
		if (pollObservingTick > sleepPollObservingTick){
			sleepPollObservingTick++;
		}
		else{
			Log.d("TEST", "requestPollObserving");
			enqueueRequest(observingRequest);
			sleepPollObservingTick = 0;
		}
	}
	private Request createPollObservingRequest(){
		return Request.createRequest(SERVICE_ID_POLL_OBSERVING, ServiceUtil.makeRequestId(SERVICE_ID_POLL_OBSERVING));
	}
}
